package chapter5.homework.oldSeries.Q3updateJPG;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.MessageDigest;

/**
 * @Project ：learn_java
 * @File ：Server.java
 * @IDE ：IntelliJ IDEA
 * @Author ：自由如风间
 * @Date ：2025/3/17 17:00
 * @Description ：
 * 接收来自客户端的文件上传申请，
 * 如果文件是jpg格式的图片且小于2MB则接收，否则拒绝且返回具体信息
 */

public class Server {

    // 验证文件哈希
    private static boolean verifyFileHash(File file, String clientHash) throws Exception {
        MessageDigest md = MessageDigest.getInstance("MD5");
        try (FileInputStream fis = new FileInputStream(file)) {
            byte[] buffer = new byte[8192];
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                md.update(buffer, 0, bytesRead);
            }
            byte[] serverHashBytes = md.digest();
            StringBuilder sb = new StringBuilder();
            for (byte b : serverHashBytes) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString().equals(clientHash);
        }
    }

    public static void main(String[] args) {

        String savePath = ".\\testIO\\photo";

        /*
          文件存储路径处理
         */
        File path = new File(savePath);
        if (!path.exists()) {
            System.out.println("路径不存在");
            boolean mkdir = path.mkdir();
            if (mkdir) {
                System.out.println("路径已创建");
            } else {
                System.out.println("路径创建出错，程序终止");
                return;
            }
        }

        try (ServerSocket ss = new ServerSocket(10000)) {
            System.out.println("服务器已启动，等待客户端连接...");
            try (Socket socket = ss.accept()) {
                System.out.println("客户端已连接");

                // 构建IO
                // 字节流，用于传输图片、视频格等式的文件
                InputStream is = socket.getInputStream();
                OutputStream os = socket.getOutputStream();

                // 字符流，传输文本更合适
                InputStreamReader isr = new InputStreamReader(is);
                OutputStreamWriter osr = new OutputStreamWriter(os);

                // 缓冲流，它们在内部有一个缓冲区，能减少与底层数据源或目标的交互次数，从而提高读写效率
                BufferedReader br = new BufferedReader(isr);
                BufferedWriter bw = new BufferedWriter(osr);

                // DataInputStream 可从输入流中读取基本数据类型和字符串，DataInputStream 则可把基本数据类型和字符串写入输出流
                DataInputStream dis = new DataInputStream(is);
                DataOutputStream dos = new DataOutputStream(os);

                byte[] buffer = new byte[4096];
                int bytesRead;

                long startTime = System.currentTimeMillis();

                while (true) {
                    /*
                    计时一分钟后退出，防止浪费资源
                    */
                    long currentTime = System.currentTimeMillis();
                    if (currentTime - startTime >= 60_000) {
                        System.out.println("程序已运行60s，终止");
                        break;
                    }

                    // 与客户端发送顺序保持一致
                    long fileSize = dis.readLong();
                    String fileName = dis.readUTF();

                    /*
                    检验错误信息并返回给客户端
                    */
                    // 不支持大小超过2MB的文件和非JPG图片
                    if (fileSize > 2 * 1024 * 1024) {
                        dos.writeInt(2);
                        continue;
                    } else if (!fileName.endsWith(".jpg")) {
                        dos.writeInt(1);
                        continue;
                    } else {
                        // 上传要求可行的编码
                        dos.writeInt(0);
                    }

                    /*
                    接收文件
                    */
                    try (FileOutputStream fos = new FileOutputStream(new File(savePath, fileName))) {
                        long totalReceived = 0;
                        /*
                         * 之前的接收部分只有一个while和里面的fos write代码，因此出现了服务端一直卡在循环的情况
                         * 豆包给出的修改代码在这里添加了一个接收文件大小检测，因此当文件达到filesize的时候就会退出while循环
                         * 停止接收
                         */
                        while (totalReceived < fileSize) {
                            bytesRead = dis.read(buffer, 0, (int) Math.min(buffer.length, fileSize - totalReceived));
                            if (bytesRead == -1) {
                                break;
                            }
                            fos.write(buffer, 0, bytesRead);
                            totalReceived += bytesRead;
                        }
                    }

                    // 接收来自客户端的md5值
                    String clientHash = dis.readUTF();
                    /*
                    验证文件完整性
                    */
                    File receivedFile = new File(savePath, fileName);
                    // 比较md5值
                    boolean hashMatch = verifyFileHash(receivedFile, clientHash);
                    // 返回验证结果
                    dos.writeBoolean(hashMatch);
                    // 打印本地信息
                    System.out.println(hashMatch ? "接收完成" : "文件破损，请稍后重传");
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
